﻿
//	SOULWIRE LTD
//	© JUSTIN CLARKE WINDLE
//	blog.soulwire.co.uk

//	Ribbon3dDemo v1.00
//	12/08/2008 11:35

package  
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Point;
	import gs.easing.Linear;
	import gs.TweenMax;
	import org.papervision3d.cameras.FreeCamera3D;
	import org.papervision3d.core.geom.renderables.Vertex3D;
	import org.papervision3d.lights.PointLight3D;
	import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.Papervision3D;
	import org.papervision3d.render.BasicRenderEngine;
	import org.papervision3d.scenes.Scene3D;
	import org.papervision3d.view.Viewport3D;
	import soulwire.papervision3d.Ribbon3D;

	public class Ribbon3dDemo extends Sprite
	{
	
	//	————————————————————————————————————————————————————————————	————
	//	MEMBER										CLASS / DATATYPE	INIT
	//	————————————————————————————————————————————————————————————	————
		
		private static const RIBBONS:				int 				= 5;
		private static const UNIVERSE:				int 				= 1000;
		private static const EASING:				int 				= 80;
		private static const COLOURS:				Array 				= [
																			[ 0x98014E, 0xFF3399 ],
																			[ 0x145252, 0x33CCCC ],
																			[ 0x579100, 0x99FF00 ],
																			[ 0x000000, 0x333333 ],
																			[ 0xF7FF51, 0xF0BA00 ]
																		  ];
	
	//	————————————————————————————————————————————————————————————
		
		private var scene:							Scene3D;
		private var camera:							FreeCamera3D;
		private var viewport:						Viewport3D;
		private var renderer:						BasicRenderEngine;
		private var render:							BitmapData;
		private var scanlinesTexture:				BitmapData;
		private var cameraFollow:					DisplayObject3D;
		private var cameraTarget:					DisplayObject3D;
		private var cameraLookAt:					DisplayObject3D;
		private var scanlines:						Shape;
		
	//	————————————————————————————————————————————————————————————	
	
		private var ribbons:						Array 				= new Array();
		private var targets:						Array 				= new Array();
		private var blurring:						BlurFilter 			= new BlurFilter( 10, 10, 3 );
		
	//	————————————————————————————————————————————————————————————
	//													 CONSTRUCTOR
	//	————————————————————————————————————————————————————————————
		
		public function Ribbon3dDemo() 
		{
			init3D();
			create3DObjects();
			createScanlines();
			drawScanlines();
			
			addEventListener( Event.ENTER_FRAME, render3D );
			Papervision3D.VERBOSE = false;
		}
		
	//	————————————————————————————————————————————————————————————
	//														 METHODS
	//	————————————————————————————————————————————————————————————
		
		private function init3D():void
		{
			scene = new Scene3D();
			camera = new FreeCamera3D( 16, 40 );
			renderer = new BasicRenderEngine();
			
			viewport = new Viewport3D( stage.stageWidth, stage.stageHeight, true );
			viewport.filters = [ new BlurFilter( 2, 2, 2 ) ];
			
			render = new BitmapData( stage.stageWidth, stage.stageHeight, true, 0x00000000 );
			addChild( new Bitmap( render ) );
		}
		
		private function create3DObjects():void
		{
			var light:PointLight3D = new PointLight3D();
			scene.addChild( light );
			
			var i:int;
			
			for ( i = 0; i < RIBBONS; i++) 
			{
				var material:FlatShadeMaterial = new FlatShadeMaterial( light, COLOURS[i][0], COLOURS[i][1] );
				material.doubleSided = true;
				
				var ribbon:Ribbon3D = new Ribbon3D( material, 20 + ( i * 6 ), 140 );
				scene.addChild( ribbon );
				ribbons.push( ribbon );
				
				if ( i == 0 ) continue;
				
				ribbon.x = random( -UNIVERSE, UNIVERSE );
				ribbon.y = random( -UNIVERSE, UNIVERSE );
				ribbon.z = random( -UNIVERSE, UNIVERSE );
			}
			
			for ( i = 0; i < RIBBONS; i++) 
			{
				var target:DisplayObject3D = new DisplayObject3D();
				targets.push( target );
				tween( target );
			}
			
			cameraFollow = new DisplayObject3D();
			cameraTarget = new DisplayObject3D();
			cameraLookAt = targets[0];
			tween( cameraFollow );
		}
		
		private function createScanlines():void
		{
			scanlines = new Shape();
			scanlinesTexture = new BitmapData( 1, 2, true, 0x66ffffff );
			scanlinesTexture.setPixel32( 0, 0, 0x00000000 );
		}
		
		private function drawScanlines():void
		{
			scanlines.graphics.clear();
			scanlines.graphics.beginBitmapFill( scanlinesTexture );
			scanlines.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
			scanlines.graphics.endFill();
		}
		
		private function tween( object:* ):void
		{
			if ( object == cameraFollow && Math.random() < .3)
			cameraLookAt = randomElement( targets );
			
			var b:Array = new Array();
			var d:int = random( 2, 6 );

			for (var i:int = 0; i < d * .6 << 0; i++) 
			b.push( {
					x:random( -UNIVERSE, UNIVERSE ),
					y:random( -UNIVERSE, UNIVERSE ),
					z:random( -UNIVERSE, UNIVERSE )
					} );

			TweenMax.to( object, d, {
									x:random( -UNIVERSE, UNIVERSE ),
									y:random( -UNIVERSE, UNIVERSE ),
									z:random( -UNIVERSE, UNIVERSE ),
									ease:Linear.easeNone,
									bezierThrough:b,
									onComplete:tween,
									onCompleteParams:[ object ]
									} );
									
			if ( object is Ribbon3D )
			TweenMax.to( object, d, { width:random( 8, 36 ) } );
		}
		
	//	————————————————————————————————————————————————————————————
	//														  EVENTS
	//	————————————————————————————————————————————————————————————
		
		private function render3D( e:Event ):void
		{
			for (var i:int = 0; i < ribbons.length; i++)
			{
				ribbons[i].x += (targets[i].x - ribbons[i].x) / EASING;
				ribbons[i].y += (targets[i].y - ribbons[i].y) / EASING;
				ribbons[i].z += (targets[i].z - ribbons[i].z) / EASING;
				ribbons[i].draw();
			}
			
			cameraTarget.x += (cameraLookAt.x - cameraTarget.x) / (EASING * 2);
			cameraTarget.y += (cameraLookAt.y - cameraTarget.y) / (EASING * 2);
			cameraTarget.z += (cameraLookAt.z - cameraTarget.z) / (EASING * 2);
			
			camera.x += (cameraFollow.x - camera.x) / (EASING * 2);
			camera.y += (cameraFollow.y - camera.y) / (EASING * 2);
			camera.z += (cameraFollow.z - camera.z) / (EASING * 2);
			
			var d:Number = camera.distanceTo( ribbons[0] ) / UNIVERSE;
			var f:Number = d * 25;
			
			camera.focus += ( f - camera.focus ) / EASING;
			camera.lookAt( cameraTarget );
			
			renderer.renderScene( scene, camera, viewport );
			
			render.draw( background );
			render.draw( viewport );
			
			/* Some cool but processor hungry FX
			render.applyFilter( render, render.rect, new Point(), blurring );
			render.draw( viewport, null, null, BlendMode.MULTIPLY );
			render.draw( scanlines, null, null, BlendMode.OVERLAY );
			render.draw( background, null, null, BlendMode.DARKEN );
			*/
		}
		
	//	————————————————————————————————————————————————————————————
	//													   	  HELPER
	//	————————————————————————————————————————————————————————————
		
		private function random( min:Number, max:Number = NaN ):Number
		{
			if (isNaN(max)) { max = min; min = 0; }
			return Math.random() * (max - min) + min;
		}
		
		private function randomElement( array:Array ):*
		{
			return array[ random( array.length ) << 0 ];
		}
		
	}
	
}
